
import java.io.*;
import java.net.*;
import java.util.*;
import messaging.*;
import debug.*;

public class Server extends Thread implements MessageHandler
{
	//The port this handler will listen on
	public static final int PORT = 8800;
	//The socket that will listen for incoming connections
	protected ServerSocket incoming;
	//A List of all nodes currently connected to this server
	protected TreeMap<Connection, SocketThread> clients;
	protected boolean running;
	protected Connection myConnection;

	/**
	* Constructor that builds a MessagePasser to listen for incoming connections and
	* pass messages on outgoing connections.
	* @param processMH A MessageHandler for the process using this MessagePasser, basicaly a callback function.
	* @param PORT The PORT that this MessagePasser will listen for incoming connections.
	*/
	public Server() throws IOException
	{
		incoming = new ServerSocket(PORT);
		clients = new TreeMap<Connection, SocketThread>();
		running = true;
	}
	/**
	* Listens for incoming socket connections to this node, and builds a SocketThread when
	* one is received.
	*/
	public void run()
	{
		while(running)
		{
			try
			{
				Debug.println("Waiting for new connections.");
				//Try to accept a new connection
				Socket client = incoming.accept();
				//call connectionAccepted() to handle any new connection handling
				connectionAccepted(new Connection(client), client);
			}
			catch(SocketException e)
			{
				//This is normal behavior when a connection is closed.
				Debug.println("ServerSocket closed");
			}
			catch(IOException e)
			{
				Debug.println("Could not accept a new connection: " + e);
			}
		}
	}

	/**
	* Method that runs once to set up a new connection, any servers that extend this server should
	* override this method to implement new connection handling (Optionally with a super.connectionAccepted() call).
	* @param The newConnection to be processed.
	*/
	public SocketThread connectionAccepted(Connection newConnection, Socket client)
	{
		SocketThread r = null;
		try
		{
			r = new SocketThread(client, this);
			r.start();
			//Dispatch a Thread for this connection and add it to our list
			clients.put(newConnection, r);
		}
		catch(IOException e)
		{
			Debug.println("Could not build a thread on the new connection: " + e);
		}
		return r;
	}
	
	/**
	* Receive an incoming message, checking for internal status messages.
	* @param msg The incoming message from the other node
	*/
	public void receive(Message msg)
	{
		switch(msg.getType())
		{
			default:
				Debug.println("Received a message: \n" + msg);
				break;
		}
	}
	
	/**
	* Send an outgoing message.
	* @param msg The outgoing message to Destination
	*/
	public boolean send(Message msg)
	{
		Debug.println("Sending a message: \n" + msg.toString());
		Connection destination = msg.getDestination();
		//Check to see that we have a connection to that node
		SocketThread client = clients.get(destination);
		if(client != null)
		{
			client.send(msg);
			return true;
		}
		return false;
	}
	
	/**
	* Kill all connections to this server, but do not kill the server
	*/
	public void killConnections()
	{
		//Close all open connections
		for(SocketThread client : clients.values())
		{
			if(client != null)
			{
				client.kill();
			}
			client = null;
		}
	}
	
	/**
	* Close down this server
	*/
	public void close()
	{
		//Close all open connections
		killConnections();
		try
		{
			incoming.close();
		}
		catch(Exception e)
		{}
		//Stop accepting new connections
		running = false;
	}
	
	/**
	* String representation of this server class.
	* @return This server as a String.
	*/
	public String toString()
	{
		return "----------Server----------\n" +
				incoming.toString() + "\n" + clients.toString();
	}
	
	public static void main(String [] args)
	{
		try
		{
			new Server().start();
		}
		catch(Exception e)
		{
			Debug.println("Could not build server");
		}
	}
}